package com.ipc.app.service.impl;

import java.util.LinkedList;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

import com.ipc.app.dal.dao.AddressDAO;
import com.ipc.app.dal.dao.CarDAO;
import com.ipc.app.dal.dao.InformationDAO;
import com.ipc.app.dal.doObject.AddressDO;
import com.ipc.app.dal.doObject.CarDO;
import com.ipc.app.dal.doObject.InformationDO;
import com.ipc.app.model.Address;
import com.ipc.app.model.Car;
import com.ipc.app.model.Information;
import com.ipc.app.model.Paginator;
import com.ipc.app.service.InformationService;
import com.ipc.app.util.converter.AddressConverter;
import com.ipc.app.util.converter.CarConverter;
import com.ipc.app.util.converter.InformationConverter;
import com.ipc.app.util.id.IDGenerator;

/**
 * @author haijie.zhang
 * 
 */
public class InformationServiceImpl implements InformationService {

	private AddressDAO addressDAO;

	private CarDAO carDAO;

	private InformationDAO informationDAO;

	private TransactionTemplate transactionTemplate;

	public void informationRelease(final Information information) {
		transactionTemplate
				.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED);
		transactionTemplate.execute(new TransactionCallback() {
			public Object doInTransaction(TransactionStatus arg0) {

				InformationDO informationDO = InformationConverter
						.informationCoreToDO(information);

				setCarForInformation(information, informationDO);

				String informationID = IDGenerator.idGeneration();
				informationDO.setInformationId(informationID);

				informationDO.setBookedSeatNumber(0);
				informationDAO.createInformation(informationDO);

				saveAddresses(information, informationID);

				return null;
			}
		});
	}

	/**
	 * @param information
	 */
	private void setCarForInformation(Information information,
			InformationDO informationDO) {
		// Car信息存储
		Car car = information.getCar();
		if (null != car) {
			CarDO carDO = carDAO.getCarDOByCarLicense(car.getCarLicense());
			if (null == carDO) {
				String carId = carInformationRelease(car);
				informationDO.setCarId(carId);
				return;
			}
			informationDO.setCarId(carDO.getCarId());
		}
	}

	/**
	 * @param information
	 * @param informationID
	 */
	private void saveAddresses(Information information, String informationID) {
		List<Address> addresses = information.getAddresses();
		List<AddressDO> addressDOs = new LinkedList<AddressDO>();
		for (Address address : addresses) {
			AddressDO addressDO = AddressConverter.addressCoreToDO(address);
			String addressID = IDGenerator.idGeneration();
			addressDO.setInformationID(informationID);
			addressDO.setAddressId(addressID);
			addressDOs.add(addressDO);
		}
		addressDAO.batchSave(addressDOs);
	}

	/**
	 * @param car
	 * @return
	 */
	private String carInformationRelease(Car car) {
		String carId = IDGenerator.idGeneration();
		CarDO carDO = CarConverter.carCoreToDO(car);
		carDO.setCarId(carId);
		carDAO.createDAO(carDO);
		return carId;
	}

	public Information getDetailInformation(String informationID) {
		InformationDO informationDO = informationDAO
				.getInformationById(informationID);
		if (null == informationDO) {
			return null;
		}

		List<AddressDO> addressDOs = addressDAO
				.getAddressByInformationID(informationID);

		Information information = InformationConverter.informationDOToCore(
				informationDO, addressDOs);

		String carID = informationDO.getCarId();

		if (StringUtils.isNotBlank(carID)) {
			CarDO carDO = carDAO.getCarDOByID(carID);
			Car car = CarConverter.carDOToCore(carDO);
			information.setCar(car);
		}
		return information;
	}

	public Information lockInformation(final String informationID) {
		transactionTemplate
				.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_MANDATORY);
		Information information = (Information) transactionTemplate
				.execute(new TransactionCallback() {
					public Object doInTransaction(TransactionStatus arg0) {
						InformationDO informationDO = informationDAO
								.lockInformation(informationID);
						Information information = InformationConverter
								.informationDOToCore(informationDO, null);
						return information;
					}
				});
		return information;
	}

	public void updateInformation(final Information information) {
		transactionTemplate
				.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED);
		transactionTemplate.execute(new TransactionCallback() {

			public Object doInTransaction(TransactionStatus arg0) {
				InformationDO informationDO = InformationConverter
						.informationCoreToDO(information);
				informationDAO.updateInformation(informationDO);
				return null;
			}
		});
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ipc.app.service.InformationService#getInformationByCity(java.lang
	 *      .String, int)
	 */
	public Paginator<Information> getInformationByCity(String city,
			String informationType, int pageIndex) {

		int informationItems = informationDAO.getInformationCountByCity(city,
				informationType);

		if (informationItems > 0) {
			List<InformationDO> informationDOs = informationDAO
					.getLastestInformationDOByPagerAndCity(city,
							informationType, Paginator.DEFAULT_ITEMS_PER_PAGE,
							pageIndex);
			Paginator<Information> informations = this.assembleInformstions(
					informationDOs, pageIndex, informationItems);

			return informations;
		}
		return null;
	}

	public Paginator<Information> getInformationBetween2City(String departure,
			String destination, String informationType, int pageIndex) {
		int informationItems = informationDAO.getInformationCountBetweenCities(
				departure, destination, informationType);
		if (informationItems > 0) {
			List<InformationDO> informationDOs = informationDAO
					.getInformationBetween2City(departure, destination,
							informationType, Paginator.DEFAULT_ITEMS_PER_PAGE,
							pageIndex);
			Paginator<Information> informations = this.assembleInformstions(
					informationDOs, pageIndex, informationItems);
			return informations;
		}
		return null;
	}

	public Paginator<Information> getRoundInformation(Double startX,
			Double endX, Double startY, Double endY, String informationType,
			int pageIndex) {

		int informationItems = informationDAO.getRoundInformationCount(startX,
				endX, startY, endY, informationType);

		if (informationItems > 0) {
			List<InformationDO> informationDOs = informationDAO
					.getRoundInformation(startX, endX, startY, endY,
							informationType, Paginator.DEFAULT_ITEMS_PER_PAGE,
							pageIndex);
			Paginator<Information> informations = this.assembleInformstions(
					informationDOs, pageIndex, informationItems);
			return informations;
		}
		return null;
	}

	public Paginator<Information> getInformationByDepAndDes(Double dep_startX,
			Double dep_endX, Double dep_startY, Double dep_endY,
			Double des_startX, Double des_endX, Double des_startY,
			Double des_endY, String informationType, int pagerIndex) {
		int informationItems = informationDAO.getInformationByDepAndDesCount(
				dep_startX, dep_endX, dep_startY, dep_endY, des_startX,
				des_endX, des_startY, des_endY, informationType,
				Paginator.DEFAULT_ITEMS_PER_PAGE, pagerIndex);
		if (informationItems > 0) {
			List<InformationDO> informationDOs = informationDAO
					.getInformationByDepAndDes(dep_startX, dep_endX,
							dep_startY, dep_endY, des_startX, des_endX,
							des_startY, des_endY, informationType,
							Paginator.DEFAULT_ITEMS_PER_PAGE, pagerIndex);
			Paginator<Information> informations = this.assembleInformstions(
					informationDOs, pagerIndex, informationItems);
			return informations;
		}
		return null;
	}

	public Paginator<Information> getInformationByUserID(String userID,
			String informationType, int pageIndex) {
		int informationItems = informationDAO.getInformationCountByUserId(
				userID, informationType);
		if (informationItems > 0) {
			List<InformationDO> informationDOs = informationDAO
					.getInformationByUserID(userID, informationType,
							Paginator.DEFAULT_ITEMS_PER_PAGE, pageIndex);
			Paginator<Information> informations = this.assembleInformstions(
					informationDOs, pageIndex, informationItems);
			return informations;
		}
		return null;
	}

	private Paginator<Information> assembleInformstions(
			List<InformationDO> informationDOs, int pageIndex,
			int informationItems) {
		Paginator<Information> informations = new Paginator<Information>();
		List<Information> informationList = new LinkedList<Information>();
		for (InformationDO informationDO : informationDOs) {
			String informationID = informationDO.getInformationId();
			List<AddressDO> addressDOs = addressDAO
					.getAddressByInformationID(informationID);
			Information information = InformationConverter.informationDOToCore(
					informationDO, addressDOs);
			informationList.add(information);
		}
		informations.setPage(pageIndex);
		informations.setItems(informationItems);
		informations.setObject(informationList);
		return informations;
	}

	public void setExpiredInformation(final String status) {
		transactionTemplate
				.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED);
		transactionTemplate.execute(new TransactionCallback() {

			public Object doInTransaction(TransactionStatus arg0) {
				informationDAO.setExpiredInformation(status);
				return null;
			}
		});
	}

	public void setAddressDAO(AddressDAO addressDAO) {
		this.addressDAO = addressDAO;
	}

	public void setCarDAO(CarDAO carDAO) {
		this.carDAO = carDAO;
	}

	public void setInformationDAO(InformationDAO informationDAO) {
		this.informationDAO = informationDAO;
	}

	public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
		this.transactionTemplate = transactionTemplate;
	}

}
